﻿using System;
using System.Collections.Generic;
using System.Linq;
using Engine;
using Engine.Graphics;
using Engine.Serialization;
using Game;
using GameEntitySystem;
using TemplatesDatabase;

namespace Mekiasm
{
    public class ComponentMekModel : Component
    {
        public SubsystemSky m_subsystemSky;

        public bool IsSet;

        public bool Animated = false;

        public ComponentFrame m_componentFrame;

        public Model m_model;

        public Matrix?[] m_boneTransforms;

        public float m_boundingSphereRadius;

        public float? Opacity
        {
            get;
            set;
        }

        public Vector3? DiffuseColor
        {
            get;
            set;
        }

        public Vector4? EmissionColor
        {
            get;
            set;
        }

        public Model Model
        {
            get
            {
                return m_model;
            }
            set
            {
                SetModel(value);
            }
        }

        public Texture2D TextureOverride
        {
            get;
            set;
        }

        public virtual Func<bool> OnAnimate
        {
            get;
            set;
        }

        public bool CastsShadow
        {
            get;
            set;
        }

        public int PrepareOrder
        {
            get;
            set;
        }

        public virtual ModelRenderingMode RenderingMode
        {
            get;
            set;
        }

        public int[] MeshDrawOrders
        {
            get;
            set;
        }

        public bool IsVisibleForCamera
        {
            get;
            set;
        }

        public Matrix[] AbsoluteBoneTransformsForCamera
        {
            get;
            set;
        }

        public virtual Matrix? GetBoneTransform(int boneIndex)
        {
            return m_boneTransforms[boneIndex];
        }

        public virtual void SetBoneTransform(int boneIndex, Matrix? transformation)
        {
            m_boneTransforms[boneIndex] = transformation;
        }

        public virtual void CalculateAbsoluteBonesTransforms(Camera camera)
        {
            ProcessBoneHierarchy(Model.RootBone, camera.ViewMatrix, AbsoluteBoneTransformsForCamera);
        }

        public virtual void CalculateIsVisible(Camera camera)
        {
            if (camera.GameWidget.IsEntityFirstPersonTarget(base.Entity))
            {
                IsVisibleForCamera = false;
                return;
            }

            float num = MathUtils.Sqr(m_subsystemSky.VisibilityRange);
            Vector3 vector = m_componentFrame.Position - camera.ViewPosition;
            vector.Y *= m_subsystemSky.VisibilityRangeYMultiplier;
            if (vector.LengthSquared() < num)
            {
                BoundingSphere sphere = new BoundingSphere(m_componentFrame.Position, m_boundingSphereRadius);
                IsVisibleForCamera = camera.ViewFrustum.Intersection(sphere);
            }
            else
            {
                IsVisibleForCamera = false;
            }
        }

        public virtual void SetModel(Model model)
        {
            m_model = model;
            if (m_model != null)
            {
                m_boneTransforms = new Matrix?[m_model.Bones.Count];
                AbsoluteBoneTransformsForCamera = new Matrix[m_model.Bones.Count];
                MeshDrawOrders = Enumerable.Range(0, m_model.Meshes.Count).ToArray();
            }
            else
            {
                m_boneTransforms = null;
                AbsoluteBoneTransformsForCamera = null;
                MeshDrawOrders = null;
            }
        }

        public virtual void ProcessBoneHierarchy(ModelBone modelBone, Matrix currentTransform, Matrix[] transforms)
        {
            Matrix m = modelBone.Transform;
            if (m_boneTransforms[modelBone.Index].HasValue)
            {
                Vector3 translation = m.Translation;
                m.Translation = Vector3.Zero;
                m *= m_boneTransforms[modelBone.Index].Value;
                m.Translation += translation;
                Matrix.MultiplyRestricted(ref m, ref currentTransform, out transforms[modelBone.Index]);
            }
            else
            {
                Matrix.MultiplyRestricted(ref m, ref currentTransform, out transforms[modelBone.Index]);
            }

            foreach (ModelBone childBone in modelBone.ChildBones)
            {
                ProcessBoneHierarchy(childBone, transforms[modelBone.Index], transforms);
            }
        }

        public struct SourceModelVertex
        {
            public float X;

            public float Y;

            public float Z;

            public float Nx;

            public float Ny;

            public float Nz;

            public float Tx;

            public float Ty;
        }
        public struct InstancedVertex
        {
            public float X;

            public float Y;

            public float Z;

            public float Nx;

            public float Ny;

            public float Nz;

            public float Tx;

            public float Ty;

            public float Instance;
        }
        public List<Common.MekModelMesh> ModelDatas = new List<Common.MekModelMesh>();
        public List<VertexBuffer> VertexBuffers = new List<VertexBuffer>();
        public SubsystemModelsRenderer SubsystemModelsRenderer;
        public List<IndexBuffer> IndexBuffers = new List<IndexBuffer>();
        public BoundingBox BoundingBox { get { return Model.CalculateAbsoluteBoundingBox(AbsoluteBoneTransformsForCamera); } }
        public Vector3 Position { get { return m_componentFrame.Position; } }
        public int Direction = 0;
        public virtual void Animate()
        {
            SetBoneTransform(Model.RootBone.Index, m_componentFrame.Matrix);
        }
        public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap)
        {
            m_subsystemSky = base.Project.FindSubsystem<SubsystemSky>(throwOnError: true);
            m_componentFrame = base.Entity.FindComponent<ComponentFrame>(throwOnError: true);
            string value = valuesDictionary.GetValue<string>("ModelName");
            string value2 = valuesDictionary.GetValue("ModelType", "Engine.Graphics.Model");
            Type type = TypeCache.FindType(value2, skipSystemAssemblies: true, throwIfNotFound: true);
            Model = (Model)ContentManager.Get(type, value);
            CastsShadow = valuesDictionary.GetValue<bool>("CastsShadow");
            string value3 = valuesDictionary.GetValue<string>("TextureOverride");
            TextureOverride = (string.IsNullOrEmpty(value3) ? null : ContentManager.Get<Texture2D>(value3));
            PrepareOrder = valuesDictionary.GetValue<int>("PrepareOrder");
            m_boundingSphereRadius = valuesDictionary.GetValue<float>("BoundingSphereRadius");
            string valuePairs = valuesDictionary.GetValue<string>("ModelData");
            if (!string.IsNullOrEmpty(valuePairs))
            {
                string[] vs = valuePairs.Split(new char[] { '|' });
                foreach (var c in vs)
                {
                    if (string.IsNullOrEmpty(c)) continue;
                    string[] pm = c.Split(new char[] { ',' });
                    ModelMesh modelMesh = Model.FindMesh(pm[0]);
                    int ind = Model.FindBone(pm[0]).Index;
                    if (pm.Length == 2)
                    {
                        ModelDatas.Add(new Common.MekModelMesh(pm[0], ind, CreateInstancedModelData(modelMesh, ind), ContentManager.Get<Texture2D>("Mekiasm/Textures/Model/" + pm[1])) { Mesh = modelMesh});
                    }
                    else
                    {
                        ModelDatas.Add(new Common.MekModelMesh(pm[0], ind, CreateInstancedModelData(modelMesh, ind), TextureOverride) { Mesh=modelMesh});
                    }
                }
            }
            SubsystemModelsRenderer = Project.FindSubsystem<SubsystemModelsRenderer>();
        }
        public InstancedModelData CreateInstancedModelData(ModelMesh modelMesh, int index)
        {
            DynamicArray<InstancedVertex> dynamicArray = new DynamicArray<InstancedVertex>();
            DynamicArray<ushort> dynamicArray2 = new DynamicArray<ushort>();
            foreach (ModelMeshPart meshPart in modelMesh.MeshParts)
            {
                _ = dynamicArray.Count;
                VertexBuffer vertexBuffer = meshPart.VertexBuffer;
                IndexBuffer indexBuffer = meshPart.IndexBuffer;
                ReadOnlyList<VertexElement> vertexElements = vertexBuffer.VertexDeclaration.VertexElements;
                ushort[] indexData = BlockMesh.GetIndexData<ushort>(indexBuffer);
                Dictionary<ushort, ushort> dictionary = new Dictionary<ushort, ushort>();
                if (vertexElements.Count != 3 || vertexElements[0].Offset != 0 || !(vertexElements[0].Semantic == VertexElementSemantic.Position.GetSemanticString()) || vertexElements[1].Offset != 12 || !(vertexElements[1].Semantic == VertexElementSemantic.Normal.GetSemanticString()) || vertexElements[2].Offset != 24 || !(vertexElements[2].Semantic == VertexElementSemantic.TextureCoordinate.GetSemanticString()))
                {
                    throw new InvalidOperationException("Unsupported vertex format.");
                }

                SourceModelVertex[] vertexData = BlockMesh.GetVertexData<SourceModelVertex>(vertexBuffer);
                for (int j = meshPart.StartIndex; j < meshPart.StartIndex + meshPart.IndicesCount; j++)
                {
                    ushort num = indexData[j];
                    if (!dictionary.ContainsKey(num))
                    {
                        dictionary.Add(num, (ushort)dynamicArray.Count);
                        InstancedVertex item = default(InstancedVertex);
                        SourceModelVertex sourceModelVertex = vertexData[num];
                        item.X = sourceModelVertex.X;
                        item.Y = sourceModelVertex.Y;
                        item.Z = sourceModelVertex.Z;
                        item.Nx = sourceModelVertex.Nx;
                        item.Ny = sourceModelVertex.Ny;
                        item.Nz = sourceModelVertex.Nz;
                        item.Tx = sourceModelVertex.Tx;
                        item.Ty = sourceModelVertex.Ty;
                        item.Instance = modelMesh.ParentBone.Index;
                        dynamicArray.Add(item);
                    }
                }

                for (int k = 0; k < meshPart.IndicesCount / 3; k++)
                {
                    dynamicArray2.Add(dictionary[indexData[meshPart.StartIndex + 3 * k]]);
                    dynamicArray2.Add(dictionary[indexData[meshPart.StartIndex + 3 * k + 1]]);
                    dynamicArray2.Add(dictionary[indexData[meshPart.StartIndex + 3 * k + 2]]);
                }
            }

            InstancedModelData instancedModelData = new InstancedModelData();
            instancedModelData.VertexBuffer = new VertexBuffer(InstancedModelData.VertexDeclaration, dynamicArray.Count);
            instancedModelData.IndexBuffer = new IndexBuffer(IndexFormat.SixteenBits, dynamicArray2.Count);
            instancedModelData.VertexBuffer.SetData(dynamicArray.Array, 0, dynamicArray.Count);
            instancedModelData.IndexBuffer.SetData(dynamicArray2.Array, 0, dynamicArray2.Count);
            return instancedModelData;
        }
    }
}
